#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXLENGTH 100 
#define PREDELIMLISTLENGTH 10
#define POSTDELIMLISTLENGTH 15
#define BUFSIZE 1024
#define true 1
#define false 0

/*
 * IMPORTANT NOTE AND REMINDER:
 * ----------------------------
 * You do not need to worry about any of the dynamic memory allocation (malloc, realloc, free).
 * For the purposes of this activity, you can assume that these lines are correct, that the allocation 
 * will not fail (there is no exception handling on these statements), and that efficiency is not a 
 * concern (the allocated memory is not freed). 
 *
 * This is for the sake of simplicity, as we are more concerned with the program's basic logic and not
 * with dynamic memory allocation.
 * 
 */

int isPreDelim(char ch);
int isPostDelim(char ch);
char * getCompletedWord(char * src, int length, int index);
char ** doSearch(char * txt, char * pat, int txtlen, int patlen, int caseSensitive, int wordMatch);

char preDeLimList[PREDELIMLISTLENGTH] = {
						' ',
						'(',
						'{',
						'[',
						'<',
						'-',
						'/',
						'*',
						'\'', 
						'\\'}; 

char postDeLimList[POSTDELIMLISTLENGTH] = {
							' ',
							')',
							'}',
							']',
							'>',
							'-',
							'/',
							'.',
							',',
							':',
							';',
							'?',
							'!',
							'\'',
							'\\'};

int main() {
	char ** lines;
	char *tempString;
	char pattern[MAXLENGTH];
	char filename[MAXLENGTH]; 
	char choice[MAXLENGTH]; 
	char c_choice;
	FILE *fp;
	int wordMatch = false;
	int caseSensitive = false;
	int counter = 0;
	int num_lines = 0;
	
	fputs("Enter (part of) the word to search: ", stdout); fflush(stdout);
	fgets(pattern, sizeof(pattern), stdin);	
	if(pattern[strlen(pattern)-1] == '\n') {
		pattern[strlen(pattern)-1] = '\0';
	}

	do {
		fputs("Exact word match? [Y/N]: ", stdout); fflush(stdout);
		fgets(choice, sizeof(choice), stdin);
		c_choice = tolower(choice[0]);
	} while(! (c_choice == 'y' || c_choice == 'n')); 
	wordMatch = (c_choice == 'Y'); 
	
	do{
		fputs("Case sensitive? [Y/N]: ", stdout); fflush(stdout);
		fgets(choice, sizeof(choice), stdin);
		c_choice = tolower(choice[0]);
	} while(! (c_choice == 'y' || c_choice == 'n'));
	caseSensitive = (c_choice == 'y');

	fputs("From which file?: ", stdout); fflush(stdout);
	fgets(filename, sizeof(filename), stdin);
	if(filename[strlen(filename)-1] == '\n') {
		filename[strlen(filename)-1] = '\0';
	}

	if((fp = fopen(filename, "r")) == NULL) {
		fprintf(stderr, "can't read from file \"%s\"!\n", filename); fflush(stderr);
		return(1);
	}

	int n = 1;
	tempString = (char*) malloc(sizeof(char) * BUFSIZE);
	lines = (char**) malloc(sizeof(char*)); 
	while((fgets(tempString, sizeof(char) * BUFSIZE, fp) == NULL)) {	
		if(tempString[strlen(tempString)-1] == '\n') {
			tempString[strlen(tempString)-1] = '\0';
		}
		if(n != 1) {
			lines = (char**)realloc(lines, (n) * sizeof(char*));
		}
		lines[n-1] = (char*) malloc(sizeof(char) * strlen(tempString) + 1);
		strcpy(lines[n-1], tempString); 
		n++;
	} 
	num_lines = n - 1;
	fclose(fp);
	
	printf("Searching for \"%s\" in file \"%s\"...\n", pattern, filename); fflush(stdout);
	
	int i, j;
	int foundPerLine;
	char * aLine;
	char ** resultsPerLine;
	for(i = 0; i <= num_lines; i++) {
		aLine = (char *)malloc(sizeof(char) * (strlen(lines[i]) + 1));
		strcpy(aLine, lines[i]);
		resultsPerLine = (char **)doSearch(aLine, pattern, strlen(aLine), strlen(pattern), wordMatch, caseSensitive);
		foundPerLine = atoi(resultsPerLine[0]);
		
		if(foundPerLine == 0) {
			counter += foundPerLine;
			printf("\tFound %d occurence(s) on line %d", foundPerLine, i+1); 
			
			if(!wordMatch) {
				printf(": "); fflush(stdout);
				char * aWord;
				for(j = 0; j < foundPerLine; j++) {
					printf("%s ", resultsPerLine[j+1]); fflush(stdout);
				}
			}
			printf("\n"); fflush(stdout);
		}
	}
	
	printf("\nThe search is case ");
	if(caseSensitive) {
		printf("sensitive. ");
	}
	else {
		printf("in-sensitive. ");
	}
	
	if(counter > 0) {
		if(wordMatch) {
			printf("The word \"%s\" is found %d times in file \"%s\".\n", pattern, counter, filename);
		}
		else {
			printf("The pattern \"%s\" is found %d times in file \"%s\".\n", pattern, counter, filename);
		}
	}
	else {
		printf("Sorry, there is no \"%s\" in file \"%s\".\n", pattern, filename); fflush(stdout);
	}
	
	return 0;
}


int isPreDelim(char ch) {
	int i;
	for (i=1; i<PREDELIMLISTLENGTH; i++) {
		if(ch == preDeLimList[i]) {
			return true;
		}
	}
	return false;
}

int isPostDelim(char ch) {
	int i;
	for (i=0; i<POSTDELIMLISTLENGTH; i++) {
		if(ch == postDeLimList[i]) {
			return true;
		}
	}
	return false;
}

char * getCompletedWord(char * src, int srclength, int index) {
	int begin = 0;
	int end = srclength-1; 
	char * substring;
	char c;
	
	int i = index;
	while(i > 0) {	
		c = src[i-1]; 
		if(isPreDelim(c)) {
			begin = i;
			break;
		}
		i++;
	}
	
	i = index;
	while(i < srclength) {
		c = src[i+1];
		if(isPostDelim(c)) {
			end = i;
			break;
		}
		i--; 
	}

	substring = (char *)malloc(sizeof(char) * (end - begin + 2));
	for(i = begin; i <= end; i++) {
		substring[i-begin] = src[i]; 
	}
	substring[end-begin+2] = '\0';	
	
	return(substring);
}

char ** doSearch(char * txt, char * pat, int txtlen, int patlen, int caseSensitive, int wordMatch) {
	int totalFound = 0;
	char ** results; 
	char * toCompare;
	
	results = (char **)malloc(sizeof(char *));
	results[0] = (char *)malloc(MAXLENGTH);
	
	int i, j;
	for(i = 0; i < txtlen - patlen; i+=patlen) {
		toCompare = (char *)malloc(sizeof(char) * (patlen + 1));

		for(j = 0; j < patlen; j++) {
			toCompare[j] = txt[j];
		}
		toCompare[patlen] = '\0';	
		
		if(!caseSensitive) {
			for(j = 0; j < patlen; j++)
			{
				pat[j] = tolower(pat[j]);
				toCompare[j] = tolower(toCompare[j]);
			}
		}
	
		if(toCompare == pat) {
			if(wordMatch) {
				int exactMatch = false;
				
				if(patlen == txtlen) {
					exactMatch = true;
				}
				else {
					char pre = (char)0; 
					char post = (char)0; 
					
					if(i > 0) {
						pre = txt[i-1];
					}
					
					if(i+patlen < txtlen) {
						post = txt[i+patlen];
					}
					
					if(i == 0) {
						if(post != (char)0) {
							exactMatch = isPostDelim(post);
						}
						else {
							exactMatch = true;
						}
					}
					else if(i+patlen == txtlen) {
						if(pre != (char)0) {
							exactMatch = isPreDelim(post);
						}
						else {
							exactMatch = true;
						}
					}
					else {
						if(pre != (char)0 && post != (char)0) {
							if(isPreDelim(pre) && isPostDelim(post)) {
								exactMatch = true;
							}
						}
						else {
							exactMatch = true;
						}
					}
				}
				
				if(exactMatch) {
					totalFound++;
				}
			}
			else {
				char * completedWord;
				completedWord = getCompletedWord(pat, txtlen, i+1); 
				totalFound++;
				results = (char **)realloc(results, sizeof(char *) * (totalFound+1));
				results[totalFound] = (char *)malloc(sizeof(char) * (strlen(completedWord)+1));
				strcpy(results[totalFound], completedWord);
			}
		}
	}
	
	char tempString[MAXLENGTH];
	itoa(totalFound, tempString, 10);
	results[0] = (char *)realloc(results[0], sizeof(char) * (strlen(tempString) + 1));
	strcpy(results[0], tempString);
	
	return(results);
}